\documentclass{article}
\usepackage[utf8]{inputenc} 
\usepackage{graphicx}
\usepackage[francais]{babel}

\usepackage{listings}
\lstset{
%language=C,
basicstyle=\footnotesize,
numbers=left,
numberstyle=\normalsize,
breaklines=true,  
numbersep=7pt,
frame=single,
}

\title{Rapport de Projet Tutoré\\OpenGL Objet}
\author{Cavelan Aurélien\\Faure Benjamin\\Henry Julien\\Hurault Jean-Christophe\\Lacourte Barbadaux Nicolas\\Lubineau Laurent\\Masson Alexandre\\Rousseau Léo}
\date{8/04/2011}

\begin{document}

\maketitle
\newpage

\section*{Remerciements}
		\paragraph{Mr Sylvain Jubertie}
		Nous remercions Mr Jubertie de nous avoir offert l'opportunité de pouvoir travailler sur 
		l'openGL en projet tutoré. Ainsi que d'avoir été disponible, pour nous aider dans l'avancée du projet.
		Nous le remercions aussi pour nous avoir mis à disposition des codes sources et des tutoriels.
		
		\paragraph{Mr Emmanuel Melin}
		Nous remercions également Mr Melin pour ses cours sur l'OpenGL qui nous ont servis de base au projet.
		
\newpage
\tableofcontents
\newpage

	\section{Introduction}
		\subsection{Sujet}
		La bibliothèque OpenGL est constituée d’un ensemble de fonctions permettant d’accéder aux fonctionnalités 3D des cartes graphiques. 
Ces fonctions sont nombreuses (déclaration de formes, textures, lumières, shaders, etc) et possèdent beacoup d'arguments, ce qui rend l'utilisation d'OpenGL difficile. 
Par exemple pour un simple cube, il faut environ 26 lignes de code sans compter la gestion du repère où l'on se trouve. L’objectif de ce projet est de simplifier la 
programmation OpenGL en introduisant une sur-couche objet qui doit être la plus simple possible et permettant l’exploitation des dernières possibilités des cartes 
graphiques. De plus la sur-couche objet ne devra pas nuire aux performances par rapport à une implémentation OpenGL classique. 



	\section{Analyse du projet}
		\subsection{Fonctionnalités Importantes}
		L'analyse du sujet, après définition des priorités, permet de faire ressortir les questions suivantes.
		Comment abstraire un objet3D, une image, créer un éclairage, comment voir et se déplacer et comment charger des données?
		Autant de questions auxquelles nous devions apporter une solution. L'objectif du projet est donc de proposer une libraire
		qui permette de créer et gérer une scène, de manière simple, sans la complexité de la syntaxe OpenGL.
		
		\subsection{Conception de l'Architecture}
		
		\newpage
		\begin{figure}
			\centering
			\includegraphics[scale=0.4]{./DiagGeneral.png}
			\caption{Diagramme général présentant les différentes couches d'abstraction}
			\label{L'architecture de la librairie}
		\end{figure}

		\newpage

	\section{Le Développement}
	\subsection {La gestion des ressources}

Nous appelons "ressources" toutes les données importées depuis l’extérieur d’un programme comme par exemple une image (.JPG, .png, etc), ou encore un modèle 3D (Mesh : .obj, .off, etc). Ces données peuvent prendre beaucoup de place en mémoire, c’est pourquoi il a fallu mettre en place une solution pour qu’elles ne soient chargées qu’une, et une seule fois en mémoire, puis détruites lorsqu’elles ne sont plus utilisées.

La solution  que nous avons développé se décompose en deux classes :

L’interface IResource permet de définir une ressource dans la librairie. Elle est identifiée par un nom unique (nom du fichier chemin inclus) et possède un compteur de références qui indique le nombre de pointeurs sur la ressource utilisés à un instant t.

L’autre classe est le ResourceManager, c’est lui qui va stocker toutes les ressources et qui va permettre de les récupérer. Pour récupérer une ressource, il suffit d’utiliser la fonction get$\langle T \rangle$(filename), qui renverra un pointeur sur une IResource. On pourra faire par exemple :

\begin{lstlisting}
Resource Manager *rsmgr = ResourceManager::getInstance();
Texture *t = rsmgr->get<Texture>("mario.bmp");
\end{lstlisting}
Si la ressource existe, la fonction se chargera simplement de renvoyer un pointeur sur IResource et d‘incrémenter le compteur de référence de la IResource (en effet il y a à présent une instance de plus), sinon elle renverra un pointeur sur NULL. Dans ce cas on pourra charger la ressource puis l’ajouter au ressource manager.

C’est une manière simple pour l’utilisateur de gérer les ressources, de plus il n’a pas à se préoccuper de la gestion de la mémoire car tout est géré en interne. La seule chose qu’il doit impérativement penser à faire une fois qu’il ne se sert plus d’une ressource est d’appeler la fonction release, par exemple : 
\begin{lstlisting}
texture->release(); 
\end{lstlisting}
Cet appel va décrémenter le compteur de références, si celui-ci arrive à 0 alors la ressource va se retirer du ResourceManager (remove) et libérer la mémoire (delete).
		
		\subsection{Device et Event Manager}
		\subsubsection{La fenêtre}
		Nous avons décidé de nous appuyer sur la librairie GLFW pour la gestion de la fenêtre en contexte OpenGL. Afin d'éviter au programmeur l'utilisation d'une
		librairie qu'il ne connaitpas forcément nous l'avons surcouché de manière transparente pour l'utilisateur. Tout se fait grâce à la classe GlowDevice. Cette
		classe est un singleton. Pour ouvrir une fenêtre il faut créer l'instance de GlowDevice au début du main (et penser à la détruire à la fin). Ensuite il suffit 
		d'appeler la fonction init avec en paramètre la longueur, la largeur et un booléen indiquant si on utilise le mode plein écran. De plus cette classe permet
		aussi de nommer la fenêtre et de récupérer le nombre de FPS (Frame Per Second). Elle permet aussi de redimensionner la fenêtre.
		
		\subsubsection{La gestion des évènements}
		La gestion des évènements se fait via la classe EventManager. Cette classe est aussi un singleton et s'appuie aussi sur la GLFW. Les évènements ne sont pas
		stoqués dans une pile mais traités en temps réél. Celà permet d'optimiser la réactivité du programme, malgré, parfois, de lours calculs. Pour éviter d'empiler
		les évènements à chaque fois qu'une touche clavier est activée, on passe par un tableau indéxé par le code de chaque touche et contenant une valeur booléenne.
		Pour tester si un évènement s'est produit, il suffit donc de regarder la case du tableau dont l'index correspond au code de la touche testée. Les codes de 
		chaque touches sont définis dans la GLFW, mais cette librairie nous permet aussi de tester directement avec le caractère correspondant à la touche. Nous avons 
		surcouché ce test grâce à deux fonctions. Elles peuvent prendre indifférement en paramètre soit un caractère soit la définition GLFW d'une touche.
		Il faut diffiérencier isKeyDown et isKeyDownOnce.\\
		La première fonction permet d'utiliser un système de key-repeat. C'est à dire que tant que la touche reste enfoncée, la fonction renvoie true.\\
		A l'inverse la seconde fonction renvera true seulement au moment de la production de l'évènement. Par la suite elle renvera false, même si la touche reste
		enfoncée.\\
		
		\subsection{Mesh}
		
		\subsubsection{Images 2D}  
		Pour avoir des images en deux dimensions, nous avons une classe Image contenant 
		un pointeur sur Texture. La texture est chargée via son nom de fichier et
		ses dimensions sont stockées dans l'image. Initialement, la position de l'image est en 0.0.
		L'Image peut être déplacée et déformée via diverses fonctions. La fonction draw() permet d'afficher
		l'Image dans la scène en trois dimensions.
		
			\subsubsection{Concept d'objets 3D}  
L'idée de base du Mesh était de créer un objet en trois dimensions générique, que l'on pourrait facilement charger, puis dessiner.
		Nous voulions un objet qui soit facile à utiliser , qui serait hors de la complexité d'OpenGL. Pour celà nous avons créé deux
		objets, le MeshData qui contient tous les tableaux de points, de normales, d'indices, ainsi que la taille de ceux-ci. Cet objet 
		est contenu dans notre autre objet, le Mesh qui contient en plus des tableaux, une texture, une position en trois dimensions,
		une rotation sur les trois axes x, y et z , un angle, un matériau pour les éclairages. Cet objet contient les observateurs 
		et les assesseurs nécessaires à sa modification complète. Avec le Mesh, nous pouvons facilement charger un objet à partir d'un fichier
		et le dessiner en tapant monobjet.draw(), au lieu de devoir le coder avec les fonctions OpenGL. 
		
			\subsubsection{Chargement de Fichiers de Points}  
		Pour le chargement des fichiers nous avons créé un chargeur de fichiers virtuel le ILoader, qui contient une fonction load.
		Elle permet de charger n'importe quel type de fichiers. Ensuite nous avons dérivé des Loaders spécifiques pour chaque
		type de fichiers. Ces classes créent un MeshData *, mettent a jour, en accord avec le fichier, les différents tableaux 
		de données, puis renvoient le pointeur au constructeur de Mesh , qui récupère les tableaux.
		Il suffit donc pour ajouter un type de fichier chargeable, de créer un nouveau Loader qui prend un nom de fichier en paramètre
		et qui renvoi un MeshData *  en modifiant les tableaux. Pour que ce chargeur soit pris en compte par la librairie, se référer à
		la partie suivante.
		
		\begin{lstlisting}
template<class T>
class SHARED ILoader
{

	public :
		virtual T* load(std::string ext) = 0;

	private :

};
		\end{lstlisting}		
		
			\subsubsection{Le Mesh-Factory}
			\paragraph{MeshFactory}
			La MeshFactory, est la classe qui permet de créer des Meshs. Elle contient une std::map$\langle string, ILoader* \rangle$ qui est le lien 
			entre les extensions de fichiers et les chargeurs correspondants à ces fichiers. Elle contient deux fonctions et la map.
			Ces fonctions sont registerMeshLoader(std::string ext, ILoader$\langle MeshData \rangle$ *loader), qui va remplir la map, et donc permettre 
			de stocker le lien entre un type de fichier et un Loader. La seconde fonction est la fonction Mesh *create(std::string filename),
			qui prend en paramètre le nom de fichier. La Factory va renvoyer TheMap["mon extension"]::create(filename), qui renvoye le 
			Mesh créé car TheMap["MonExtension"] vas retourner le Loader associé au fichier.
			
			\paragraph{MeshFactoryBuilder}
			C'est le remplisseur de la Factory, c'est lui qui utilise la fonction register. il va donc permettre de dire à la librarie, 
			qu'une extension est liée à un chargeur. on déclare une instance statique pour qu'il se créé et charge la map dès le debut du programme
			et la remplisse. Pour rajouter un nouveau type de fichier chargeable: 
			Créer un fichier MONEXTBuilder.h contenant une classe monextBuilder, il faut inclure le header du chargeur de votre type de fichier 
			à charger, mettre la ligne 
\begin{lstlisting}			
MeshFactory::registerMeshLoader("monExt", new LoaderMONEXT()); 
\end{lstlisting}
dans le constructeur de la classe,
			déclarer une instance statique en dehors de la classe et inclure votre nouveau header dans votre main.\\
			Exemple d'ajout de nouvelle extension chargeable :
			\begin{lstlisting}
#include "LoaderMYEXTENSION.h"
class SHARED MyExtensionBuilder
{
public :
	
	MyExtensionBuilder()
	{
		MeshFactory::registerMeshLoader(".myextension", new LoaderMYEXTENSION());
	}		
};

static MyExtensionBuilder TheMyExtensionBuilder;
			\end{lstlisting}			

			
		\subsection{Skybox} 
			Nous avons voulu rendre les fenêtres d'openGL plus agréable, le fond noir, par défaut, ne faisant pas très "fini" nous avons créé une Skybox. Qu'est 
	ce qu'une Skybox ?
		\subsubsection{Définition d'une Skybox}
		Une skybox est un gros cube constitué de 6 textures sur lesquelles sont projetés un ciel, des montagnes, des bâtiments éloignés, etc ... recréant ainsi 
		l'illusion d'un environnement éloigné en 3D. Toute l'astuce est dans le fait que la caméra observatrice doit être situé au centre de ce gros cube. 
		Comment fonctionne t-elle ?
		\subsubsection{Fonctionnement de la Skybox}
		La partie visible de la scène doit être dessinée à l'intérieur de la skybox, pour cela on désactive le test de profondeur et donc on a l'impression que tous 
		les objets sont dessinés à l'intérieur et que le fond est situé à l'infini. Cette box aura une largeur, longueur et hauteur identique et toutes les faces du cube seront 
		dirigées vers l'intérieur. La box aura donc une position fixe quel que soit la position de la camera, l'orientation sera, évidement, indépendante de la direction 
		de la caméra. Afin de permettre l'illusion nous avons besoin de faire bouger la box de la même façon que la caméra. Comment l'avons nous programmé ?
		\subsubsection{Programmation de la Skybox}
		Maintenant que nous avons donné une forme à notre Skybox, il faut maintenant lui donner des couleurs. Pour cela, nous allons utiliser des textures. Celles-ci ne 
		seront ni à une dimension, ni à deux ... ni même à trois ... mais sera une texture cube appelée : "CubeMap", on peut la placer à mi-chemin entre une texture 2D et 
		une texture 3D. Elle peut se représenter, comme son nom l'indique, par un cube composé de 6 faces. Chacune des faces correspond à une direction par rapport au 
		centre du cube soit les faces X Positif (devant), X Négatif (derriére), Y Positif (haut), Y Négatif (bas), Z Positif (gauche) et enfin Z Négatif (droite). Ainsi, 
		chacune d'elle sera une texture 2D. Donc une texture CubeMap correspond à six textures 2D mais contient moins d'information qu'une texture 3D. 	
		
		\subsection{Camera}
		Nous avons créé une interface ICamera.
L'idée est d'avoir une caméra générique pour ensuite créer des classes héritières et les implémentations de différentes caméras selon la volonté du programmeur.

Nous avons défini un exemple : la classe Camera. C'est une caméra FreeFly.

Comment implémenter une caméra ? ICamera contient une map nommée KeyMap qui contient un évènement associé à une touche du clavier et une map KeyStates qui contient un évènement associé à un booléen pour définir si cet évènement est actif ou non.
Il faut redéfinir la méthode setDefaultKeyMap() pour associer les touches que nous souhaitons à un évènement(par exemple : ``avancer'', ``reculer'', etc).
Il faut redéfinir la méthode getEvent() pour vérifier si les différents évènements sont actifs ou non.
Enfin, il faut redéfinir la méthode animate() pour mettre des conditionnelles et gérer les déplacements de la caméra.
		\subsection{Lumières et matériaux}
			Afin de rendre nos objets beaucoup plus réel nous avons fait une classe Light qui gère les lumières, l'éclairage de la scène, et une classe Material qui sert à 
	définir la manière dont l'objet réagi avec la lumière. Cela donne une vraie consistance aux objets, on peut ainsi donner beaucoup d'effet sur les objets (ombres, 
	reflets, etc.)
		\subsubsection{Lumières}
		Nous gérons les effets de lumière en utilisant une classe Light, cette classe contient quatres tableaux de valeur, en GLfloat, pour contenir la valeur de la 
		lumière ambiente (concerne les objets et les lampes, c'est la lumière qui a tellement été dispersée et renvoyée par l'environnement qu'il est impossible de 
		déterminer la direction d'où elle émane. Elle semble venir de toutes les directions. Quand une lumiére ambiante rencontre une surface, elle est renvoyée dans 
		toutes les directions), la lumière diffuse (concerne les objets et les lampes, c'est la lumière qui vient d'une direction particulière, et qui va étre plus 
		brillante si elle arrive perpendiculairement à la surface que si elle est rasante. Par contre, après avoir rencontré la surface, elle est renvoyée uniformément 
		dans toutes les directions), la lumière spéculaire (concerne les objets et les lampes, la lumiére spéculaire vient d'une direction particuliére et est renvoyée 
		par la surface dans une direction particulière. Par exemple un rayon laser réfléchi par un miroir) et enfin la position de la lumière.\\
		Ces valeurs seront utilisées dans les fonctions d'openGL pour gérer la lumière. Nous pouvons instancier jusqu'à huit lumières, les déplacer et changer leur valeurs. 
		Evidemment la lumière n'agit sur la scène que si les objets on des normales, celles-ci sont calculées dans les chargeurs d'objets. Notre classe hérite 
		de IDrawable car nous surdéfinissons la fonction draw() dans la lumière afin de dessiner une sphère jaune autour de la source lumineuse et ainsi la visualiser facilement.
		Nous avons fait une fonction "on" et "off" qui active et désactive la lumiere.
	
		\subsubsection{Matériaux}
		Nous avons géré les effets de matière sur les objets. Pour cela nous avons créé un objet matière qui possède trois tableaux de GLfloat, un tableau
		de matière ambiante, de matière diffuse et de matière spéculaire. Ces tableaux s'occuperont de gérer ce que reflète l'objet, ainsi on peut en mettant 
		des valeurs précises, donner un rendu métal, plastique, etc. à l'objet. Chaque objet possède sa propre matière, cette matière est déclarer dans le mesh
		de l'objet.
	
		
		\subsection{Shaders}
				Les shaders sont une seconde classe permetant de gérer les lumières et les ombrages. Pour l'implémenter nous avons un 
		ShaderLoader, héritant de ILoader$\langle ShaderBase \rangle$
		construisant une classe intermédiaire, la ShaderBase. Cette classe représente un
		VertexShader ou un FragmentShader. La classe Shader assemble ensuite deux ShaderBase
		pour créer un Program. Si le chargement ou la compilation des shaders échoue, l'utilisateur
		recevra un message d'erreur.
		En utilisant la fonction setShader(bool) on peut finalement définir si oui ou non le Shader est actif. 
		Note : un Shader incluant un VertexShader et un FragmentShader est aussi appelé un Program.

		\subsection{Autres classes utiles}
Nous avons défini des classes utiles pour la librairie : Color3, Color4, Vector2 et Vector3.
Color3 et Color4, comme leur nom l'indique servent à définir une couleur (RGB pour Color3 et RGBA pour Color4).
Vector2 et Vector3, comme leur nom l'indique servent à définir un vecteur. Vector 2 sert à créer un vecteur de 2 dimensions et Vector3 de 3 dimensions. De plus, Vector3 possède des méthodes utiles pour normaliser un vecteur, pour faire un produit scalaire et pour calculer la norme d'un vecteur.
Toutes les classes sont dans un template permettant de définir le type de chaque variable qui composent la couleur ou le vecteur. De plus, nous avons fait des typedefs pour une meilleure lisibilité dans le code. Ainsi, au lieu d'écrire Color3$\langle int \rangle$, il suffira d'écrire Color3i, ou bien au lieu d'écrire Vector3$\langle double \rangle$, il suffira d'écrire Vector3d.
La prochaine étape dans le développement de la librairie serait de créer une classe Matrix contenant une matrice 4x4 qu'on appelle aussi quaternion. Elle serait utile pour optimiser la gestion des transformations et cela corresponderait à la nouvelle norme d'OpenGL 3.0.

	
	\section{Fonctionnalités non Traitées La Portabilité OpenGL ES}

		Cette version d'OpenGL est faite pour les plateformes embarquées comme les systèmes android, iOS ou Symbian. La librairie OpenGL ES est une version d'openGL dont 
les besoins en puissance ont été considérablement réduits du fait de la limitation matérielle de ces plateformes.
Il y a eu plusieurs versions d'OpenGL ES. la première est OpenGL ES 1.0 qui est dérivée d'OpenGL 1.3. Il s'agit en fait d'une spécification de moteur de rendu 3D 
logiciel multiplate-forme. Ensuite il y a eu une version 1.1 qui dérive d'OpenGL 1.5. Cette version a été réalisée pour s'adapter à l'arrivée de nouveau matériels
des mobiles qui sont plus puissant.Enfin il y a la version 2.0 qui dérive d'OpenGL 2.0. Cette version est arrivée peu avant les prototypes de processeurs 
embarqués haut de gamme. Elle s'allège de la plupart de ses fonctions précédentes dont toute son API de rendu à pipeline fixe pour laisser la place à un système 
de shaders basé sur une variation de GLSL.
Nous n'avons pas gérés la portabilité en OpenGL ES par manque de temps.
De plus la difficulté avec cette version d'OpenGL est que toutes les plate-formes sont dites compatible avec GLES 
mais pour beaucoup d'entre elles, cela ne fonctionne pas. D'ou la difficulté de pouvoir tester le bon fonctionnement de ce nous aurions pu faire. 

	
	\section{Conclusion}
		\subsection{Ce que le Projet nous a Apporté}
		Ce projet nous a apporté beaucoup. En effet, pour beaucoup d'entre nous, nous ne connaissions pas OpenGL au commencement du projet. Les premiers temps
		ont donc été consacré à la familiarisation avec cette API. Cela nous à montré deux principes importants dans l'informatique. Le premier est l'autonomie,
		en effet nous nous sommes mis à travailler sur OpenGL via diverses documentation et tutoriaux de manière autonome. Le second est le travail et l'entraide en
		groupe. Tout au long de cet apprentissage nous avons partagé les ressources que nous trouvions utile et nous nous sommes entraidés pour progresser de manière
		homogène au sein du groupe. Une fois que nous étions suffisament à l'aise avec OpenGL nous avons travaillé à la conception et l'architecture de la librairie.
		Une fois de plus nous avons pu constater tout l'intérêt des méthodes d'analyse apprises à l'IUT. Au final ce projet nous a permis de prendre des responsabilité
		et de nous exprimer pleinement dans la réalisation d'une réponse à un problème formulé par un commanditaire, c'est, au final, une excélente simmulation de ce
		qui nous attendra dans notre avenir professionnel.
		\subsection{Perspectives d'Evolutions}
		Nous espérons sincèrement avoir répondu aux attentes de M. Jubertie avec ce projet. Cependant, nous avons beaucoup apprécié de travailler dessus et nous
		souhaitons continuer à développer notre librairie, pourquoi pas sous forme de logiciel libre. Nous voulons lui intégrer d'autres fonctionnalités telles
		que la génération de terrains aléatoire à l'aide de l'algorithme du Bruit de Perlin, ou bien la porter sur OpenGL ES ou encore l'adapter aux normes d'openGL 3.0.
		
	
\end{document}
